<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>赵元杰的技术博客</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://zhaoyouqi.github.io/"/>
  <updated>2019-06-27T09:06:30.018Z</updated>
  <id>https://zhaoyouqi.github.io/</id>
  
  <author>
    <name>赵元杰</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>kotlin 开篇</title>
    <link href="https://zhaoyouqi.github.io/2019/06/27/kotlin-opening/"/>
    <id>https://zhaoyouqi.github.io/2019/06/27/kotlin-opening/</id>
    <published>2019-06-27T08:50:47.000Z</published>
    <updated>2019-06-27T09:06:30.018Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Kotlin-开篇"><a href="#Kotlin-开篇" class="headerlink" title="Kotlin 开篇"></a>Kotlin 开篇</h1><p>这篇文章是为「码上开学」写的，首发于<a href="https://kaixue.io/kotlin-overview/" target="_blank" rel="noopener">码上开学官网 </a>。</p><h2 id="引子"><a href="#引子" class="headerlink" title="引子"></a>引子</h2><blockquote><p>找到那把三叉戟，你便能号令整个海洋。     ——《海王》</p></blockquote><h2 id="为什么需要新语言"><a href="#为什么需要新语言" class="headerlink" title="为什么需要新语言"></a>为什么需要新语言</h2><p>Java是当今世界最流行的工业级语言，有着非常成熟的生态和广泛的开发群体。当初Android选择Java作为开发语言，也是为了吸引Java程序员这个世界上最大的开发群体。最早的一批Android程序员可能已经用Java写了近十年Android程序了，各种实践方法也比较成熟了。那么今天我们为什么还需要一门新语言呢？这就要从Java的发行历史说起了。</p><h2 id="Java历史"><a href="#Java历史" class="headerlink" title="Java历史"></a>Java历史</h2><table><thead><tr><th>Java版本</th><th>发布日期</th><th>重要功能</th></tr></thead><tbody><tr><td>JDK 1.0</td><td>Jan 1996</td><td>Initial release</td></tr><tr><td>JDK 1.1</td><td>Feb 1997</td><td>Reflection, JDBC, Inner Classes</td></tr><tr><td>J2SE 1.2</td><td>Dec 1998</td><td>Collection, JIT</td></tr><tr><td>J2SE 1.3</td><td>May 2000</td><td>HotSpot JVM, JNDI</td></tr><tr><td>J2SE 1.4</td><td>Feb 2002</td><td>assert, Regular, NIO</td></tr><tr><td>J2SE 5.0</td><td>Sep 2004</td><td>Generics, Annotations, Autoboxing/unboxing, Concurrency</td></tr><tr><td>Java SE 6</td><td>Dec 2006</td><td>JDBC 4.0, Java Compiler API, New GC</td></tr><tr><td>Java SE 7</td><td>July 2011</td><td>Strings in switch, Diamond operator, Resource management</td></tr><tr><td>Java SE 8</td><td>Mar 2014</td><td>Lambda, Functional interface, Optionals, New Date and Time API</td></tr><tr><td>Java SE 9</td><td>Sep 2017</td><td>Multi-gigabytes heaps, Java Module System</td></tr></tbody></table><p>其实从Java 5开始，Java就是一门很完备的工业级语言了，生态也非常成熟，各种框架层出不穷。但是后续的Java 6和Java 7只是在Java 5基础上添加了一些功能和语法糖，根本算不上大版本更新，我觉得版本号定为5.1，5.2可能更合适。如果你稍微了解过同期的C#，看看Lambda和LINQ，你大概也会对Java怒其不争吧。</p><p>直到2014年Java 8正式发布，才算是Java语言的一次大更新，加入了呼声很高的Lambda和stream。可是等等，Android是哪一年发布的？<a href="https://en.wikipedia.org/wiki/Android_version_history" target="_blank" rel="noopener">2008年</a>！所以Android是以Java 6来进行开发。虽然从Android Studio 3.0开始可以<a href="https://developer.android.com/studio/write/java8-support" target="_blank" rel="noopener">使用部分Java 8特性进行开发</a>了，但是非常好用的<a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html" target="_blank" rel="noopener">stream</a>只能在Android 7.0以上使用（即minSdkVersion = 24）。受困于Android版本碎片化，我们只能放弃。那么用Java 6写代码到底有什么问题呢？</p><h2 id="从一道算法题说起"><a href="#从一道算法题说起" class="headerlink" title="从一道算法题说起"></a>从一道算法题说起</h2><p>请听题：有一个英文小写单词列表List\&lt;String>，要求将其按首字母分组（key为 ‘a’ - ‘z’），并且每个分组内的单词列表都是按升序排序，得到一个Map\&lt;Character, List\&lt;String>>。请尝试用10行以内Java 6.0代码完成。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">List&lt;String&gt; keywords = ...;</span><br><span class="line">Map&lt;Character, List&lt;String&gt;&gt; result = <span class="keyword">new</span> HashMap&lt;&gt;();</span><br><span class="line"><span class="keyword">for</span> (String k: keywords) &#123;</span><br><span class="line">    <span class="keyword">char</span> firstChar = k.charAt(<span class="number">0</span>);</span><br><span class="line">    <span class="keyword">if</span> (!result.contains(firstChar)) &#123;</span><br><span class="line">        result.put(firstChar, <span class="keyword">new</span> ArrayList&lt;String&gt;());</span><br><span class="line">    &#125;</span><br><span class="line">    result.get(firstChar).add(k);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (List&lt;String&gt; list: result.values()) &#123;</span><br><span class="line">    Collections.sort(list);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>实际上已经超过10行了。我们再看看业界标杆C#是怎么写的</p><figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">List&lt;<span class="keyword">string</span>&gt; keywords = …;</span><br><span class="line"><span class="keyword">var</span> result = keywords</span><br><span class="line">        .GroupBy(k =&gt; k[<span class="number">0</span>])</span><br><span class="line">        .ToDictionary(</span><br><span class="line">            g =&gt; g.Key,</span><br><span class="line">            g =&gt; g.OrderBy(k =&gt; k).ToList());</span><br></pre></td></tr></table></figure><p>为了代码可读性，我添加了一些换行。如果你愿意的话，写成一行也行。可以明显看出，对比当今先进语言，Java 6已经无法让人愉快的写代码。用Java 6写代码时，我们脑子里想的不是要<strong>做什么</strong>，而是<strong>怎么做</strong>。</p><h2 id="Java平台"><a href="#Java平台" class="headerlink" title="Java平台"></a>Java平台</h2><p>我们平时说Java，其实包含了两个不同的概念：一是Java语言本身，二是Java虚拟机，即JVM。虽然上面吐槽了Java语言本身的历史包袱，但是JVM还是非常优秀的，它有非常多的优点：</p><ul><li>跨平台</li><li>自动垃圾回收</li><li>运行速度快（你没看错。虽然Java程序速度没有C/C++快，但是在所有编程语言中Java属于速度快的那一拨。大数据框架<a href="https://hadoop.apache.org/" target="_blank" rel="noopener">Hadoop</a>便是用Java开发，能顺利处理海量数据。）</li></ul><p>我们知道Java代码编译后会生成<a href="https://en.wikipedia.org/wiki/Java_bytecode" target="_blank" rel="noopener">字节码</a>，然后字节码在JVM中运行注意Android中的虚拟机并不是JVM，而是Dalvik/ART，但也是编译成字节码。那么有没有可能新<strong>创造</strong>一门语言，编译的时候也生成字节码，然后在JVM中运行呢？这样既可以摆脱Java的历史包袱，又能享受到JVM和成熟Java框架的各种好处！答案当然是肯定的！实际上Java平台已经衍生出<a href="https://www.scala-lang.org/" target="_blank" rel="noopener">Scala</a>、<a href="https://clojure.org/" target="_blank" rel="noopener">Clojure</a>、<a href="http://groovy-lang.org/" target="_blank" rel="noopener">Groovy</a>等比较流行的语言了。而今天我们要讲的<a href="http://kotlinlang.org" target="_blank" rel="noopener">Kotlin</a>则是Java平台中的新贵，出自大名鼎鼎的<a href="https://www.jetbrains.com/" target="_blank" rel="noopener">JetBrains</a>公司。打开他们的官网你就会发现，很多著名的IDE（比如<a href="https://www.jetbrains.com/idea/" target="_blank" rel="noopener">IntelliJ</a>、<a href="https://www.jetbrains.com/ruby/" target="_blank" rel="noopener">RubyMine</a>、<a href="https://www.jetbrains.com/webstorm/" target="_blank" rel="noopener">WebStorm</a>）都是出自这家公司。IntelliJ是当今最主流的Java IDE，JetBrains公司在开发IntelliJ的过程中积累的经验令Kotlin的诞生显得水到渠成。而Google在2017年的I/O大会上<a href="https://techcrunch.com/2017/05/17/google-makes-kotlin-a-first-class-language-for-writing-android-apps/" target="_blank" rel="noopener">宣布</a>Kotlin成为Android开发的<a href="https://developer.android.com/kotlin/" target="_blank" rel="noopener">官方编程语言</a>后，更是令Kotlin一夜之间成为最受瞩目的编程语言之一。那么我们来看看Kotlin会给我们带来哪些好处吧。</p><h2 id="Kotlin带来的好处"><a href="#Kotlin带来的好处" class="headerlink" title="Kotlin带来的好处"></a>Kotlin带来的好处</h2><h3 id="Data-Class"><a href="#Data-Class" class="headerlink" title="Data Class"></a>Data Class</h3><p>假设你在做一个金融交易系统，需要定义一个Class来表示每一笔支付，包含金额和币种。这里有一个简单的例子：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Purchase</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> String currency;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> price; <span class="comment">//为了便于演示，这里将价格设为整数类型</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>看起来不错。但是作为一个经验丰富的程序员，你一眼就发现了潜藏的问题。一个金融系统，一定是要保证每笔交易的正确性的，你肯定不希望object中的currency和price的值在某个模块里面被粗心的人修改了。所以你需要一个Immutable Class，即不可更改的类。于是你做了如下修改：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Purchase</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String currency;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> price;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Purchase</span><span class="params">(String currency, <span class="keyword">int</span> price)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.currency = currency;</span><br><span class="line">        <span class="keyword">this</span>.price = price;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getCurrency</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> <span class="keyword">this</span>.currency; &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getPrice</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> <span class="keyword">this</span>.price; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>你将currency和price定义为private field，通过构造方法来赋值，并且只暴露get方法。这样就不用担心数据被其他人误修改了。完美！可是等等，这就够了吗？熟读《Java编程思想》的你立刻想起，一个完备的Class还需要重写<code>equals()</code>、<code>hashCode()</code>和<code>toString()</code>方法！于是你又添加了一些代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Purchase</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">equals</span><span class="params">(Object o)</span> </span>&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">hashCode</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我就问你烦不烦:) 仅仅是表示金额和币种，怎么要写那么多代码？如果有十几个字段，那还不得上百行代码呀？！请看Kotlin中优雅的实现：</p><figure class="highlight kt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">data</span> <span class="class"><span class="keyword">class</span> <span class="title">Purchase</span></span>(<span class="keyword">val</span> currency: String, <span class="keyword">val</span> price: int)</span><br></pre></td></tr></table></figure><p>没了。</p><p>一行代码搞定:)</p><p>关键字<code>val</code>会自动为currency和price创建不可更改的field，而<code>data</code>则会帮我们自动生成equals()、hashCode()和toString()方法！这还不是全部，看下面的代码：</p><figure class="highlight kt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> iPhone8 = Purchase(“CNY”, <span class="number">5888</span>)</span><br><span class="line"><span class="keyword">val</span> iPhoneX = iPhone8.copy(price = <span class="number">8888</span>)</span><br></pre></td></tr></table></figure><p>看到<code>copy</code>方法了吗？是不是巨好用？这也是data class为我们自动生成的，方便吧:)</p><h3 id="Extension-Functions"><a href="#Extension-Functions" class="headerlink" title="Extension Functions"></a>Extension Functions</h3><p>Kotlin中的<a href="http://kotlinlang.org/docs/reference/extensions.html" target="_blank" rel="noopener">Extension</a>类似于Objective-C中的<a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Category.html" target="_blank" rel="noopener">Category</a>，可以帮助我们扩展已有的Class，而无需继承这个Class，无论我们能不能访问源码。这对于系统Class以及一些第三方library中的Class特别有帮助。</p><p>随着项目规模的扩大，你的代码里肯定少不了一系列Utils类（比如<code>FileUtils.java</code>，<code>DateUtils.java</code>）来封装一些繁琐但常用的功能。比如在Android开发中，如果我们要动态的调整一个View的宽高，必须要先将dp转为px，所以我们会有这样一个方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ScreenUtil</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">dip2px</span><span class="params">(<span class="keyword">float</span> dipValue)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>) (dipValue * density + <span class="number">0.5f</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后这样使用：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">layoutParams.width = ScreenUtil.dip2px(<span class="number">16F</span>)</span><br></pre></td></tr></table></figure><p>这样看起来没什么不对，只是不太符合人的阅读习惯。看看用Extension能帮我们做些什么：</p><figure class="highlight kt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="built_in">Int</span>.<span class="title">toPx</span><span class="params">()</span></span>: <span class="built_in">Int</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> ScreenUtil.dip2px(<span class="keyword">this</span>.toFloat())</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们给整数类型添加了一个Extension Function，叫做<code>toPx</code>，用来实现dp到px的转换。然后优雅的调用：</p><figure class="highlight kt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">layoutParams.width = <span class="number">16</span>.toPx()</span><br></pre></td></tr></table></figure><p>可读性是不是好多了🙂。你可能会想，Extension一个一个自己写也挺麻烦的，有没有大神把常用的Extension Functions封装成一个library啊？有的，Google爸爸已经帮我们考虑到了😘。请参看<a href="https://developer.android.com/kotlin/ktx" target="_blank" rel="noopener">Android KTX</a>项目。这个项目是<a href="https://developer.android.com/jetpack/" target="_blank" rel="noopener">Android Jetpack</a>的一部分，而Jetpack也是我们接下来要分享的内容。</p><h3 id="Null-Safety"><a href="#Null-Safety" class="headerlink" title="Null Safety"></a>Null Safety</h3><p>这是一个值得吹上三天三夜的革新。在Quora上有这样一个问题：<a href="https://www.quora.com/Why-was-the-Null-Pointer-Exception-in-Java-called-a-billion-dollar-mistake" target="_blank" rel="noopener">为什么空指针异常被称为10亿美金的错误？</a>相信每一个Java程序员都对这个问题深有体会。看看我们为了避免程序崩溃，不得不写多么丑陋的代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (a != <span class="keyword">null</span> &amp;&amp; a.b != <span class="keyword">null</span> &amp;&amp; a.b.c != <span class="keyword">null</span>) &#123;</span><br><span class="line">    println(a.b.c.toString());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样写有两个问题，一是代码很丑陋，二是本少爷很容易忘记做空指针检查啊😤！！！Java 8引入了<a href="https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html" target="_blank" rel="noopener">Optional</a>来解决这个问题，比自己检查空指针要稍微优雅一点，但仍然有许多不必要的代码包装。</p><p>Kotlin的类型系统从一开始就致力于避免空指针异常。我们在Kotlin中定义变量时可以指定它为可空类型（Nullable Type）和不可空类型（Non-Null Type）。</p><figure class="highlight kt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a: String = <span class="string">"abc"</span> <span class="comment">// 不可空</span></span><br><span class="line">a = <span class="literal">null</span> <span class="comment">// 编译报错</span></span><br><span class="line">print(a.length) <span class="comment">// 没问题</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b: String? = <span class="string">"abc"</span> <span class="comment">// 可空</span></span><br><span class="line">b = <span class="literal">null</span> <span class="comment">// 没问题</span></span><br><span class="line">print(b.length) <span class="comment">// 编译报错</span></span><br><span class="line">print(b?.length) <span class="comment">// 没问题</span></span><br></pre></td></tr></table></figure><p>上例的a是Non-Null，而b是Nullable。他们的唯一区别是定义的时候b的<code>String</code>后面加了个问号<code>?</code>，代表它是Nullable的。只有Nullable类型可以赋值为null。调用Nullable对象的方法时，需要加一个问号，像<code>b?.length</code>。如果b为null，那么<code>print(b?.length)</code>这行代码不会被执行。这样设计有什么好处呢？回到我们上一个例子，如果我们将<code>a</code>，<code>a.b</code>，<code>a.b.c</code>都定义为Nullable，那么就可以这样写：</p><figure class="highlight kt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">println(a?.b?.c?.toString())</span><br></pre></td></tr></table></figure><p>如果它们中间任意一个为null，那么 <code>a?.b?.c?.toString()</code> 这个 chain 就会返回 <strong>null</strong> 。是不是很强大很方便？理论上讲，只要我们的类型定义合理，那么90%的空指针异常都是能避免。为什么我不敢说100%？因为有时候我们看设计文档确定某个值绝对不可能为null，于是给它定义为Non-Null，结果程序运行时偏偏就传过来一个null……😂</p><h3 id="Courtines"><a href="#Courtines" class="headerlink" title="Courtines"></a>Courtines</h3><p>Android开发中多线程处理一直是一个难点，稍微不小心就容易出问题。你可能已经学习过<a href="https://github.com/ReactiveX/RxJava" target="_blank" rel="noopener">RxJava</a>，并在项目中成功使用RxJava来处理线程问题。这非常好。但是如果你的业务逻辑并没有复杂到必须用RxJava来解决，你应该看看Kotlin中的<a href="https://kotlinlang.org/docs/reference/coroutines.html" target="_blank" rel="noopener">Courtines</a>。Courtines通常翻译成_协程<em>，在<a href="https://www.lua.org/" target="_blank" rel="noopener">Lua</a>等程序语言中已经有着广泛的应用。它的概念稍微有些复杂，我们可以暂时认为它是一种</em>无需锁_并且<em>没有线程切换开销</em>的轻量级线程。</p><p>我们看一个例子，假设我需要从网络取回来一些数据，然后保存到数据库，那么传统的异步回调写法是这样的：</p><figure class="highlight kt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">networkRequest &#123; result -&gt;</span><br><span class="line">   databaseSave(result) &#123; rows -&gt;</span><br><span class="line">     <span class="comment">// Result saved</span></span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>而用Courtines的写法是这样的：</p><figure class="highlight kt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> result = networkRequest()</span><br><span class="line">databaseSave(result)</span><br><span class="line"><span class="comment">// Result saved</span></span><br></pre></td></tr></table></figure><p>你可能已经发现了，这段代码根本不关心线程如何切换，只关心我到底要实现什么功能。实际上Courtines背后远比这要复杂，想要熟练使用需要经历一些学习曲线，但好在曲线并不算陡峭。我们后面会有专门文章来讲解如合使用Courtines。如果你已经激动的等不及了（和第一次知道Courtines时的我一样），可以先跟着Google Codelabs中的<a href="https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#0" target="_blank" rel="noopener">教程</a>来练练手。</p><h3 id="与Java的交互"><a href="#与Java的交互" class="headerlink" title="与Java的交互"></a>与Java的交互</h3><p>虽然前面我们讲到，Java平台上的语言都会编译成字节码，但这并不代表所有语言都能与Java无缝交互。比如<a href="http://lampwww.epfl.ch/~michelou/scala/using-scala-from-java.html" target="_blank" rel="noopener">Scala和Java的交互</a>就非常复杂。幸运的是，JetBrains从一开始就将与Java的交互性作为Kotlin的设计目标之一。无论是从Java调用Kotlin，还是从Kotlin调用Java，都非常自然，没有什么障碍。这意味着我们可以任意使用Java丰富的第三方库，也可以在现有的Java工程基础上用Kotlin添加新的功能。万一你遇到某些特殊情况，有一段逻辑用Kotlin搞不定（我写这篇文章时就遇到一个），可以把这段逻辑抽出来用Java写。有Java兜底，我们就能放心的为项目引入Kotlin了。</p><h3 id="无障碍升级"><a href="#无障碍升级" class="headerlink" title="无障碍升级"></a>无障碍升级</h3><p>我们前面讲到，要想在Android开发中使用Java 8中的stream，需要设定minSdkVersion = 24。如果将来要使用Java 9的新特性，恐怕又得等Android版本更新了。Kotlin则不同，你可以把它当作一个集成到项目中的第三方library，可以随时升级到最新版本！这意味着Kotlin将来推出的更多新特性都能应用到所有Android项目中！</p><h2 id="迁移到Kotlin会遇到什么问题"><a href="#迁移到Kotlin会遇到什么问题" class="headerlink" title="迁移到Kotlin会遇到什么问题"></a>迁移到Kotlin会遇到什么问题</h2><p>前面讲了Kotlin的这么多好处，但要知道世上没有十全十美的语言。就Kotlin而言，目前社区反映比较多的问题是可见性修饰符（Visibility Modifiers）。我们知道Java中有四种访问权限：<code>public</code>，<code>protected</code>，<code>private</code>和<code>package-private</code>。其中package-private是指在同一个包名下可见，在library开发中非常方便。而在Kotlin中没有了package-private，取而代之的是<code>internal</code>，即在同一个<em>模块（Module）</em>内可见。这样我们在设计library时必须对可见性控制有更周全的考虑。为Android开发library，不可避免的要重写系统方法。如果你用重写了一个Java中的<code>package-private</code>方法，那么不好意思，这个方法会变成<code>public</code>🤣，原本并不想暴露出来的方法暴露了……</p><p>如果你主要做应用开发，那么目前已经没有什么坑了。Google已经逐渐用Kotlin重写Android文档中的所有例子，Github上用Kotlin开发的项目也在飞快增长。社区方面，在StackOverflow做的<a href="https://insights.stackoverflow.com/survey/2018/" target="_blank" rel="noopener">2018年度调查</a>中，Kotlin更是一举登上最受欢迎语言榜第二名！就像前面讲的，万一有问题还有Java兜底。所以你唯一需要考虑的可能就是团队学习成本。好在Kotlin是一门非常简易、现代的语言，相信做这个决定并不困难。</p><h2 id="回到最开始"><a href="#回到最开始" class="headerlink" title="回到最开始"></a>回到最开始</h2><p>我想肯定有好事的人要问，最开始那个算法题用Kotlin怎么写呢？</p><figure class="highlight kt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> keywords = arrayOf(<span class="string">"apple"</span>, <span class="string">"app"</span>, <span class="string">"alpha"</span>, ...)</span><br><span class="line"><span class="keyword">val</span> result = keywords</span><br><span class="line">        .groupBy &#123; it[<span class="number">0</span>] &#125;</span><br><span class="line">        .mapValues &#123; it.value.sorted() &#125;</span><br></pre></td></tr></table></figure><p>你还在等什么？</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;Kotlin-开篇&quot;&gt;&lt;a href=&quot;#Kotlin-开篇&quot; class=&quot;headerlink&quot; title=&quot;Kotlin 开篇&quot;&gt;&lt;/a&gt;Kotlin 开篇&lt;/h1&gt;&lt;p&gt;这篇文章是为「码上开学」写的，首发于&lt;a href=&quot;https://kaixue.
      
    
    </summary>
    
      <category term="Kotlin" scheme="https://zhaoyouqi.github.io/categories/Kotlin/"/>
    
    
  </entry>
  
  <entry>
    <title>Chrome Custom Tabs最佳实践</title>
    <link href="https://zhaoyouqi.github.io/2016/08/12/custom-tabs-best-practices/"/>
    <id>https://zhaoyouqi.github.io/2016/08/12/custom-tabs-best-practices/</id>
    <published>2016-08-12T08:54:34.000Z</published>
    <updated>2016-08-13T09:02:56.000Z</updated>
    
    <content type="html"><![CDATA[<p>距离Google发布Chrome Custom Tabs已经一年，Twitter、Medium等国外App早已支持了这个功能，但遗憾的是国内App鲜有支持。这篇文章以官方<a href="https://developer.chrome.com/multidevice/android/customtabs" target="_blank" rel="noopener">开发文档</a>和<a href="https://github.com/GoogleChrome/custom-tabs-client" target="_blank" rel="noopener">示例源码</a>为基础，加上自己的理解，希望能帮助读者快速掌握Chrome Custom Tabs的用法。</p><h1 id="为什么要用Chrome-Custom-Tabs？"><a href="#为什么要用Chrome-Custom-Tabs？" class="headerlink" title="为什么要用Chrome Custom Tabs？"></a>为什么要用Chrome Custom Tabs？</h1><p>当App需要打开一个网站时，开发者面临两种选择：默认浏览器或WebView。这两种选择都有不足。从App跳转到浏览器是一个非常重的切换，并且浏览器无法自定义；而WebView无法与浏览器共享cookies等数据，并且需要开发者处理非常多的场景。</p><p>Chrome Custom Tabs提供了一种新的选择，既能在App和网页之间流畅切换，又能有多种自定义选项。其实它本质上是调用了Chrome中的一个Activity来打开网页，这样想就能理解这些优点了。能自定义的项目有：</p><ul><li><p>Toolbar颜色</p></li><li><p>进场和出场动画</p></li><li><p>Toolbar上的action button，menu item和bottom toolbar（通过RemoveView实现）</p></li></ul><p>Chrome Custom Tabs还提供预启动Chrome和预加载网页内容的功能，与传统方式相比加载速度有显著提升。</p><p><img src="https://developer.chrome.com/multidevice/images/customtab/performance.gif" alt="alt"></p><h1 id="什么时候用Chrome-Custom-Tabs，什么时候用WebView？"><a href="#什么时候用Chrome-Custom-Tabs，什么时候用WebView？" class="headerlink" title="什么时候用Chrome Custom Tabs，什么时候用WebView？"></a>什么时候用Chrome Custom Tabs，什么时候用WebView？</h1><p>如果Web页面是你自己的内容（比如淘宝商品页之于手机淘宝），那么WebView是最好的选择，因为你可能需要针对网页内容及用户操作做非常多的自定义。如果是跳到一个外部网站，比如在App中点了一个广告链接跳转到广告商的网站，那么建议使用Chrome Custom Tabs。</p><h1 id="前置条件"><a href="#前置条件" class="headerlink" title="前置条件"></a>前置条件</h1><p>用户的手机上需要安装Chrome <strong>45</strong>或以上版本，并且<strong>设为默认浏览器</strong>。考虑到Chrome在国内手机上的占有率，这确实是个问题……但如果你的APP不只是面对国内市场，那么以Google在海外市场的影响力，这完全不是问题。</p><p>肯定有人要问，如果手机上没有装Chrome，调用Chrome Custom Tabs会发生什么行为呢？我们查看<code>CustomTabsIntent.Builder</code>的<a href="https://android.googlesource.com/platform/frameworks/support/+/c502e63/customtabs/src/android/support/customtabs/CustomTabsIntent.java#164" target="_blank" rel="noopener">源码</a>可以发现，Builder的内部构造了一个action为<code>Intent.ACTION_VIEW</code>的Intent，所以答案是调用默认浏览器来打开URL。</p><p>这时我们可以发现Chrome Custom Tabs的原理：如果Chrome是默认浏览器，那么这个Intent自然就会唤起Chrome，然后Chrome会根据Intent的各个Extra来配置前面所讲的自定义项。这里有一个隐藏的好处：<strong>如果你的工作恰好是开发浏览器，那么也可以根据这些Extra信息来定制界面！</strong></p><h1 id="开发向导"><a href="#开发向导" class="headerlink" title="开发向导"></a>开发向导</h1><h2 id="快速上手"><a href="#快速上手" class="headerlink" title="快速上手"></a>快速上手</h2><p>首先在你的<em>build.gradle</em>文件中加入dependency</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">dependencies &#123;</span><br><span class="line">    ...</span><br><span class="line">    compile <span class="string">'com.android.support:customtabs:24.1.1'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后写几行代码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">String url = <span class="string">"https://www.google.com"</span>;</span><br><span class="line">CustomTabsIntent.Builder builder = <span class="keyword">new</span> CustomTabsIntent.Builder();</span><br><span class="line">CustomTabsIntent customTabsIntent = builder.build();</span><br><span class="line">customTabsIntent.launchUrl(<span class="keyword">this</span>, Uri.parse(url));</span><br></pre></td></tr></table></figure><p>就好了！不费吹灰之力~</p><p>注意launchUrl这个方法的第一个参数是个Activity。肯定有人会跳起来说，我要在ViewHolder里面处理点击跳转，根本拿不到Activity，只有Context肿么办？！！（其实这个人就是我）</p><p>那么我们看看launchUrl的源码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">launchUrl</span><span class="params">(Activity context, Uri url)</span> </span>&#123;</span><br><span class="line">    intent.setData(url);</span><br><span class="line">    ActivityCompat.startActivity(context, intent, startAnimationBundle);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>原来是为了传入startAnimationBundle这个参数来实现自定义转场动画。那么我们自己处理一下就好了：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (context <span class="keyword">instanceof</span> Activity) &#123;</span><br><span class="line">    customTabsIntent.launchUrl((Activity) context, uri);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    Intent intent = customTabsIntent.intent;</span><br><span class="line">    intent.setData(uri);</span><br><span class="line">    <span class="keyword">if</span> (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.JELLY_BEAN) &#123;</span><br><span class="line">        context.startActivity(intent, customTabsIntent.startAnimationBundle);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        context.startActivity(intent);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="修改Toolbar颜色"><a href="#修改Toolbar颜色" class="headerlink" title="修改Toolbar颜色"></a>修改Toolbar颜色</h2><p>你一定希望Chrome Custom Tabs的Toolbar颜色与你自己APP的Toolbar颜色保持相同，看起来就像是在APP内打开网页一样。一行代码搞定：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">builder.setToolbarColor(colorInt);</span><br></pre></td></tr></table></figure><h2 id="添加action-button"><a href="#添加action-button" class="headerlink" title="添加action button"></a>添加action button</h2><p>你可能想要在Toolbar上加上action button，那么需要创建一个PendingIntent。下面的代码增加了一个发送邮件的action button：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Intent actionIntent = <span class="keyword">new</span> Intent(Intent.ACTION_SEND);</span><br><span class="line">actionIntent.setType(<span class="string">"*/*"</span>);</span><br><span class="line">actionIntent.putExtra(Intent.EXTRA_EMAIL, <span class="string">"example@example.com"</span>);</span><br><span class="line">actionIntent.putExtra(Intent.EXTRA_SUBJECT, <span class="string">"example"</span>);</span><br><span class="line">PendingIntent pi = PendingIntent.getActivity(<span class="keyword">this</span>, <span class="number">0</span>, actionIntent, <span class="number">0</span>);</span><br><span class="line">Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_share);<span class="comment">//注意在正式项目中不要在UI线程读取图片</span></span><br><span class="line">builder.setActionButton(icon, <span class="string">"send email"</span>, pi, <span class="keyword">true</span>);</span><br></pre></td></tr></table></figure><h2 id="添加menu-item"><a href="#添加menu-item" class="headerlink" title="添加menu item"></a>添加menu item</h2><p>Chrome Custom Tabs的menu默认包含了三个显示为图标的item(Forward, Page Info, Refresh)和两个文字item(Find in page, Open in Browser)。我们可以再添加最多5个文字item。同样需要创建PendingIntent：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Intent menuIntent = <span class="keyword">new</span> Intent();</span><br><span class="line">menuIntent.setClass(getApplicationContext(), SomeActivity.class);</span><br><span class="line">PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), <span class="number">0</span>, menuIntent, <span class="number">0</span>);</span><br><span class="line">builder.addMenuItem(<span class="string">"Menu entry 1"</span>, pi);</span><br></pre></td></tr></table></figure><h2 id="设置转场动画"><a href="#设置转场动画" class="headerlink" title="设置转场动画"></a>设置转场动画</h2><p>如果你的APP设置了转场动画，那么为了统一的用户体验，可以在Chrome Custom Tabs中设置同样的动画</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">builder.setStartAnimations(<span class="keyword">this</span>, R.anim.slide_in_right, R.anim.slide_out_left);</span><br><span class="line">builder.setExitAnimations(<span class="keyword">this</span>, R.anim.slide_in_left, R.anim.slide_out_right);</span><br></pre></td></tr></table></figure><h2 id="预启动-Warm-up-Chrome和预加载"><a href="#预启动-Warm-up-Chrome和预加载" class="headerlink" title="预启动(Warm up) Chrome和预加载"></a>预启动(Warm up) Chrome和预加载</h2><p>默认情况下，调用了<a href="http://developer.android.com/reference/android/support/customtabs/CustomTabsIntent.html#launchUrl" target="_blank" rel="noopener">CustomTabsIntent#launchUrl</a>方法之后，才会在后台启动（原文是spin up）Chrome，然后加载网页。这个过程会花费宝贵的时间，并影响到用户体验。要是能「秒开」网页那就爽了。Chrome Custom Tabs可以绑定Chrome的一个Service，绑定成功之后可以预启动Chrome，还可以让Chrome预加载一些网页（当然这是要消耗一些流量的）。大致的过程是这样的：</p><ul><li>通过<a href="http://developer.android.com/reference/android/support/customtabs/CustomTabsClient.html#bindCustomTabsService" target="_blank" rel="noopener">CustomTabsClient#bindCustomTabsService</a>方法绑定Service。绑定成功后会得到一个CustomTabsClient的instance。如果绑定失败，说明Chrome版本过低或者没有安装。</li><li>通过<a href="http://developer.android.com/reference/android/support/customtabs/CustomTabsClient.html#warmup" target="_blank" rel="noopener">CustomTabsClient#warmup</a>方法在后台启动Chrome。</li><li>通过<a href="http://developer.android.com/reference/android/support/customtabs/CustomTabsClient.html#newSession" target="_blank" rel="noopener">CustomTabsClient#newSession</a>方法创建一个新的session。这一步还可以传入一个<a href="http://developer.android.com/reference/android/support/customtabs/CustomTabsCallback.html" target="_blank" rel="noopener">CustomTabsCallback</a>参数用来得到session的状态，比如页面是否加载完成。</li><li>通过<a href="http://developer.android.com/reference/android/support/customtabs/CustomTabsSession.html#mayLaunchUrl" target="_blank" rel="noopener">CustomTabsSession#mayLaunchUrl</a>来告诉Chrome要预加载哪些网页。</li><li>当用户点击事件发生时，将前面那个session传入<a href="http://developer.android.com/reference/android/support/customtabs/CustomTabsIntent.Builder.html" target="_blank" rel="noopener">CustomTabsIntent.Builder</a>，然后用文章最开头的方法打开网页。</li></ul><p>下面是完成这个过程的简单代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> <span class="keyword">implements</span> <span class="title">ServiceConnectionCallback</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> CustomTabsSession mSession;</span><br><span class="line">    <span class="keyword">private</span> CustomTabsClient mClient;</span><br><span class="line">    <span class="keyword">private</span> CustomTabsServiceConnection mConnection;</span><br><span class="line">  </span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">bindCustomTabsService</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mConnection = <span class="keyword">new</span> ServiceConnection(<span class="keyword">this</span>);</span><br><span class="line">        CustomTabsClient.bindCustomTabsService(<span class="keyword">this</span>, <span class="string">"com.android.chrome"</span>, mConnection);</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">warmup</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mClient != <span class="keyword">null</span>) mClient.warmup(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">preLaunch</span><span class="params">(String url)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mClent != <span class="keyword">null</span> &amp;&amp; mSession == <span class="keyword">null</span>) &#123;</span><br><span class="line">            mSession = mClient.newSession(<span class="keyword">new</span> CustomTabsCallback(&#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onNavigationEvent</span><span class="params">(<span class="keyword">int</span> navigationEvent, Bundle extras)</span> </span>&#123;</span><br><span class="line">                    <span class="comment">// 这里可以取到Session的状态</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;));</span><br><span class="line">        &#125;</span><br><span class="line">        mSession.mayLaunchUrl(Uri.parse(url), <span class="keyword">null</span>, <span class="keyword">null</span>);<span class="comment">//这里的第三个参数可以传入一些低优先级的Url，但不能保证会被预加载</span></span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">launch</span><span class="params">(String url)</span> </span>&#123;</span><br><span class="line">        CustomTabsIntent.Builder builder = <span class="keyword">new</span> CustomTabsIntent.Builder(mSession);</span><br><span class="line">        CustomTabsIntent customTabsIntent = builder.build();</span><br><span class="line">        customTabsIntent.launchUrl(<span class="keyword">this</span>, Uri.parse(url));</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDestroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (mConnection != <span class="keyword">null</span>) &#123;</span><br><span class="line">            unbindService(mConnection);</span><br><span class="line">            mClient = <span class="keyword">null</span>;</span><br><span class="line">            mSession = <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">super</span>.onDestroy();</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="comment">/** ServiceConnectionCallback的回调方法 */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onServiceConnected</span><span class="params">(CustomTabsClient client)</span> </span>&#123;</span><br><span class="line">        mClient = client;</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onServiceDisconnected</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mClient = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="简单的预启动"><a href="#简单的预启动" class="headerlink" title="简单的预启动"></a>简单的预启动</h2><p>Android Support Library 24.0.0开始加入了<a href="https://developer.android.com/reference/android/support/customtabs/CustomTabsClient.html#connectAndInitialize" target="_blank" rel="noopener">CustomTabsClient#connectAndInitialize</a>方法来简化预启动代码。如果你无法预料到用户会打开哪个URL，或者出于省电的考虑不想预加载URL，那么可以在Activity的onStart中加入一行代码来预启动。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CustomTabsClient.connectAndInitialize(<span class="keyword">this</span>, <span class="string">"com.android.chrome"</span>);</span><br></pre></td></tr></table></figure><h1 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h1><h2 id="绑定Custom-Tabs的service并预启动"><a href="#绑定Custom-Tabs的service并预启动" class="headerlink" title="绑定Custom Tabs的service并预启动"></a>绑定Custom Tabs的service并预启动</h2><p>预启动Chrome可以帮助你节省高达<strong>700ms</strong>的宝贵时间！这几乎是可以划分卡与不卡的差别。启动过程是在后台以低优先级进行，所以<strong>不会对你的APP性能有负面影响</strong>。</p><h2 id="预加载网页内容"><a href="#预加载网页内容" class="headerlink" title="预加载网页内容"></a>预加载网页内容</h2><p>预加载网页后可以达到秒开的效果！所以如果你至少有50%的把握用户会打开某个URL，你应当调用<a href="http://developer.android.com/reference/android/support/customtabs/CustomTabsSession.html#mayLaunchUrl%28android.net.Uri,%20android.os.Bundle,%20java.util.List%3Candroid.os.Bundle%3E%29" target="_blank" rel="noopener">mayLaunchUrl()</a>方法。这个方法会提前下载并渲染网页内容，但不可避免的会有一点流量和电量的消耗。如果用户正在使用收费的数据流量，或者手机电量不足，那么这个方法不会生效。所以我们<strong>完全不用自己考虑性能优化</strong>。</p><h2 id="备选方案"><a href="#备选方案" class="headerlink" title="备选方案"></a>备选方案</h2><p>如果用户的手机上没有安装Chrome，那么打开默认浏览器可能并不是最好的用户体验。所以如果在bindService那一步失败了，无论是打开默认浏览器还是WebView，选择一个你认为最好的备选方案。</p><h2 id="referrer"><a href="#referrer" class="headerlink" title="referrer"></a>referrer</h2><p>很多网站都会统计自己的流量是从哪儿来的，所以最好告诉他们是你的帅气APP给他们带来了流量：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">intent.putExtra(Intent.EXTRA_REFERRER, </span><br><span class="line">        Uri.parse(Intent.URI_ANDROID_APP_SCHEME + <span class="string">"//"</span> + context.getPackageName()));</span><br></pre></td></tr></table></figure><h2 id="加入自定义动画"><a href="#加入自定义动画" class="headerlink" title="加入自定义动画"></a>加入自定义动画</h2><p>自定义的转场动画会让你的网页跳转更流畅。确保进场动画和出场动画是反向的，比如网页从右边进来，就从右边出去，这样能帮助用户理解跳转关系。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">builder.setStartAnimations(<span class="keyword">this</span>, R.anim.slide_in_right, R.anim.slide_out_left);</span><br><span class="line">builder.setExitAnimations(<span class="keyword">this</span>, R.anim.slide_in_left, R.anim.slide_out_right);</span><br></pre></td></tr></table></figure><h2 id="为Action-Button选个合适的图标"><a href="#为Action-Button选个合适的图标" class="headerlink" title="为Action Button选个合适的图标"></a>为Action Button选个合适的图标</h2><p>一个合适的图标能让用户快速的理解到APP的功能。但记住图标的最大尺寸是宽48dp高24dp。</p><h2 id="其他支持的浏览器"><a href="#其他支持的浏览器" class="headerlink" title="其他支持的浏览器"></a>其他支持的浏览器</h2><p>前面有讲到其他浏览器也有机会支持Custom Tabs的功能（虽然我还没发现有哪款已经支持了）。如果你检测到不止一个浏览器支持Custom Tabs，那么第一次调用时最好询问用户打算用哪个浏览器。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Returns a list of packages that support Custom Tabs.</span></span><br><span class="line"><span class="comment"> */</span> </span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ArrayList <span class="title">getCustomTabsPackages</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">    PackageManager pm = context.getPackageManager();</span><br><span class="line">    <span class="comment">// Get default VIEW intent handler.</span></span><br><span class="line">    Intent activityIntent = <span class="keyword">new</span> Intent(Intent.ACTION_VIEW, Uri.parse(<span class="string">"http://www.example.com"</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Get all apps that can handle VIEW intents.</span></span><br><span class="line">    List resolvedActivityList = pm.queryIntentActivities(activityIntent, <span class="number">0</span>);</span><br><span class="line">    ArrayList packagesSupportingCustomTabs = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (ResolveInfo info : resolvedActivityList) &#123;</span><br><span class="line">        Intent serviceIntent = <span class="keyword">new</span> Intent();</span><br><span class="line">        serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION);</span><br><span class="line">        serviceIntent.setPackage(info.activityInfo.packageName);</span><br><span class="line">        <span class="comment">// Check if this package also resolves the Custom Tabs service.</span></span><br><span class="line">        <span class="keyword">if</span> (pm.resolveService(serviceIntent, <span class="number">0</span>) != <span class="keyword">null</span>) &#123;</span><br><span class="line">            packagesSupportingCustomTabs.add(info);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> packagesSupportingCustomTabs;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="给用户选择权"><a href="#给用户选择权" class="headerlink" title="给用户选择权"></a>给用户选择权</h2><p>如果你的APP之前一直是用默认浏览器打开URL，后来才加入的Custom Tabs，那么老用户可能希望保留原来的习惯。可以考虑在设置里面增加一个选项，让用户自行选择。</p><h2 id="尽量让Native-APP处理URL"><a href="#尽量让Native-APP处理URL" class="headerlink" title="尽量让Native APP处理URL"></a>尽量让Native APP处理URL</h2><p>有些URL可以由Native APP处理。如果用户安装了Twitter的APP并且点击了Twitter的URL，他可能更期望用Twitter APP打开。所以在打开URL之前，检查看看手机里有没有其他APP可以处理这个URL。</p><h2 id="自定义Toolbar颜色"><a href="#自定义Toolbar颜色" class="headerlink" title="自定义Toolbar颜色"></a>自定义Toolbar颜色</h2><p>如果你希望用户觉得网页内容是你的APP的一部分，那么就将Toolbar颜色设为你的primaryColor。如果希望清除的表明网页内容与APP无关，那么选个不同的颜色吧。</p><h2 id="增加一个分享按钮"><a href="#增加一个分享按钮" class="headerlink" title="增加一个分享按钮"></a>增加一个分享按钮</h2><p>用户可能想要把URL分享给好友，但Custom Tabs默认并没有分享按钮，所以最好自己加个吧。</p><h2 id="自定义关闭按钮"><a href="#自定义关闭按钮" class="headerlink" title="自定义关闭按钮"></a>自定义关闭按钮</h2><p>Custom Tabs左上角的关闭按钮默认是一个叉叉。如果你希望用户感觉到网页内容是APP的一部分，那么最好把叉叉换成返回按钮。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">builder.setCloseButtonIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back));</span><br></pre></td></tr></table></figure><h2 id="分清内部链接和外部链接"><a href="#分清内部链接和外部链接" class="headerlink" title="分清内部链接和外部链接"></a>分清内部链接和外部链接</h2><p>举个例子，如果用户在Twitter APP里点击了一个<code>http://twitter.com</code>开头的URL，那么应该在APP内部处理。Custom Tabs只应用来处理外部链接。</p><h2 id="处理连击"><a href="#处理连击" class="headerlink" title="处理连击"></a>处理连击</h2><p>如果你想在用户点击URL到打开Custom Tabs之间的这段时间做一点准备工作，确保不要超过100ms，否则用户可能会觉得APP没有反应而再次点击。</p><p>然而你懂的在Android上是无法完全避免卡顿的，所以当用户反复点击同一个URL时，你应该只将URL打开一次。</p><h1 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h1><p>如果你的APP是使用默认浏览器打开URL，那么身为一个合格的开发者，即使你的APP只在国内上架，也应该加入Custom Tabs支持。毕竟国内还是有一些会科学上网的用户使用Chrome的。更重要的是，<strong>我们这些开发者如果看到有国内APP使用了Custom Tabs，会欣慰的点个赞！</strong></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;距离Google发布Chrome Custom Tabs已经一年，Twitter、Medium等国外App早已支持了这个功能，但遗憾的是国内App鲜有支持。这篇文章以官方&lt;a href=&quot;https://developer.chrome.com/multidevice/an
      
    
    </summary>
    
      <category term="Android" scheme="https://zhaoyouqi.github.io/categories/Android/"/>
    
    
      <category term="chrome" scheme="https://zhaoyouqi.github.io/tags/chrome/"/>
    
      <category term="customtabs" scheme="https://zhaoyouqi.github.io/tags/customtabs/"/>
    
  </entry>
  
  <entry>
    <title>Kotlin在Android中的应用</title>
    <link href="https://zhaoyouqi.github.io/2015/11/14/kotlin-in-android/"/>
    <id>https://zhaoyouqi.github.io/2015/11/14/kotlin-in-android/</id>
    <published>2015-11-14T05:13:52.000Z</published>
    <updated>2015-11-14T07:27:22.000Z</updated>
    
    <content type="html"><![CDATA[<p>最近公司新开发了一个新App，android程序员就我一个人，爱咋写咋写~~于是我尝试用Kotlin完成了这个App。开发过程中越来越感动……太好用了！！！解决了android开发中的好多痛点！</p><h1 id="dp-sp-px间的转换"><a href="#dp-sp-px间的转换" class="headerlink" title="dp, sp, px间的转换"></a>dp, sp, px间的转换</h1><p>传统的java写法通常是写个类似<code>Util.dpToPx(int value)</code>这样的方法。看看Kotlin是怎么通过<a href="https://kotlinlang.org/docs/reference/extensions.html" target="_blank" rel="noopener">Extension</a>来优雅的解决的：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="built_in">Int</span>.<span class="title">dpToPx</span><span class="params">()</span></span>: <span class="built_in">Int</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (toInt() <span class="keyword">in</span> intArrayOf(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">this</span> * Global.density).toInt() <span class="comment">//这里的Gloabl.density是在应用启动时获取的</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后就可以这样写了：<code>params.topMargin = 16.dpTpPx()</code></p><p>是不是感动到哭😭</p><h1 id="设置View的宽高"><a href="#设置View的宽高" class="headerlink" title="设置View的宽高"></a>设置View的宽高</h1><p>如果用java来写通常也是会有个Util方法<code>ViewHelper.setSize(View view, int width, int height)</code>，我每次在ViewHolder中调用一堆这样的方法都觉得很蛋疼……我们的救世主来了：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> View.<span class="title">setSize</span><span class="params">(width: <span class="type">Int</span>, height: <span class="type">Int</span>)</span></span> &#123;</span><br><span class="line">    <span class="keyword">val</span> params = layoutParams</span><br><span class="line">    params.width = width</span><br><span class="line">    params.height = height</span><br><span class="line">    layoutParams = params</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意这里的<code>layoutParams</code>是Kotlin将<code>setLayoutParams()</code>和<code>getLayoutParams()</code>自动转为了<a href="https://kotlinlang.org/docs/reference/properties.html" target="_blank" rel="noopener">Property</a>，写过C#的应该不会陌生。然后我们就可以愉快的写代码了~<code>yourView.setSize(100, 100)</code></p><h1 id="View的动画"><a href="#View的动画" class="headerlink" title="View的动画"></a>View的动画</h1><p>每次写Animation都是一个头两个大……这回我们不看java了，直接上Kotlin代码：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> View.<span class="title">animateTopMargin</span><span class="params">(valueFromInDP: <span class="type">Int</span>, valueToInDP: <span class="type">Int</span>, duration: <span class="type">Long</span> = <span class="number">300</span>)</span></span> &#123;</span><br><span class="line">    <span class="keyword">val</span> animation = <span class="keyword">object</span>: Animation() &#123;</span><br><span class="line">        <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">applyTransformation</span><span class="params">(interpolatedTime: <span class="type">Float</span>, t: <span class="type">Transformation</span>?)</span></span> &#123;</span><br><span class="line">            <span class="keyword">val</span> params = layoutParams <span class="keyword">as</span> ViewGroup.MarginLayoutParams</span><br><span class="line">            <span class="keyword">val</span> from = valueFromInDP.dpToPx()</span><br><span class="line">            <span class="keyword">val</span> to = valueToInDP.dpToPx()</span><br><span class="line">            params.topMargin = from + ((to - from) * interpolatedTime).toInt()</span><br><span class="line">            layoutParams = params</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    animation.duration = duration</span><br><span class="line">    startAnimation(animation)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>哇，duration还贴心的设了个默认值呢！然后就可以这样写了~<code>yourView.animateTopMargin(16, 32)</code>或者你想把duration设长一点<code>yourView.animateTopMargin(16, 32, 500)</code></p><h1 id="让Fresco更易用"><a href="#让Fresco更易用" class="headerlink" title="让Fresco更易用"></a>让Fresco更易用</h1><p><a href="http://github.com/facebook/fresco" target="_blank" rel="noopener">Fresco</a>是Facebook出品的网络图片加载库，实现了许多在android上非常有用的功能。比如<a href="http://frescolib.org/docs/requesting-multiple-images.html#_" target="_blank" rel="noopener">Multi-URI</a>功能可以优先显示低分辨率的图片，等高分辨率的图片下载完成后再进行替换。这个功能在移动设备上太太太有用了，就是写起来有点蛋疼：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Uri lowResUri, highResUri;</span><br><span class="line">DraweeController controller = Fresco.newDraweeControllerBuilder()</span><br><span class="line">    .setLowResImageRequest(ImageRequest.fromUri(lowResUri))</span><br><span class="line">    .setImageRequest(ImageRequest.fromUri(highResUri))</span><br><span class="line">    .setOldController(mSimpleDraweeView.getController())</span><br><span class="line">    .build();</span><br><span class="line">mSimpleDraweeView.setController(controller);</span><br></pre></td></tr></table></figure><p>于是我又祭出了神器Extension</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> SimpleDraweeView.<span class="title">setMultiUri</span><span class="params">(lowResUri: <span class="type">Uri</span>, highResUri: <span class="type">Uri</span>)</span></span> &#123;</span><br><span class="line">    <span class="keyword">val</span> newController = Fresco.newDraweeControllerBuilder()</span><br><span class="line">            .setLowResImageRequest(ImageRequest.fromUri(lowResUri))</span><br><span class="line">            .setImageRequest(ImageRequest.fromUri(highResUri))</span><br><span class="line">            .setOldController(controller)</span><br><span class="line">            .build();</span><br><span class="line">    controller = newController</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后怎么使用我就不写了，大家都知道了:smiley:</p><h1 id="焕然一新的SharedPreference"><a href="#焕然一新的SharedPreference" class="headerlink" title="焕然一新的SharedPreference"></a>焕然一新的SharedPreference</h1><p>你看到这里可能觉得不过是扩展方法嘛，Obj-C也可以做到啊，没什么嘛。哈哈哈哈图样图森破！有请<a href="https://kotlinlang.org/docs/reference/delegated-properties.html" target="_blank" rel="noopener">Delegated Properties</a>！下面这段代码来源于<a href="http://google.com/+OmarMiatello" target="_blank" rel="noopener">Omar Miatello</a>在今年米兰的GDG DevFest的<a href="https://goo.gl/7Vhy0s" target="_blank" rel="noopener">演讲</a></p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AppPreferences</span></span>(<span class="keyword">private</span> <span class="keyword">val</span> context: Context) &#123;</span><br><span class="line">    <span class="keyword">val</span> preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)</span><br><span class="line">    <span class="keyword">var</span> userName <span class="keyword">by</span> PreferenceDelegates.string(defaultValue = context.getString(R.string.user_name))</span><br><span class="line">    <span class="keyword">var</span> password <span class="keyword">by</span> PreferenceDelegates.string()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">object</span> PreferenceDelegates &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">fun</span> <span class="title">string</span><span class="params">(defaultValue: <span class="type">String</span>? = <span class="literal">null</span>)</span></span>: ReadWriteProperty&lt;AppPreferences, String?&gt; &#123;</span><br><span class="line">        <span class="keyword">return</span> PrefString(defaultValue)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">PrefString</span></span>(<span class="keyword">private</span> <span class="keyword">val</span> defaultValue: String?) : ReadWriteProperty&lt;AppPreferences, String?&gt; &#123;</span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">getValue</span><span class="params">(thisRef: <span class="type">AppPreferences</span>, property: <span class="type">KProperty</span>&lt;*&gt;)</span></span>: String? &#123;</span><br><span class="line">        <span class="keyword">return</span> thisRef.preferences.getString(property.name, defaultValue)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">setValue</span><span class="params">(thisRef: <span class="type">AppPreferences</span>, property: <span class="type">KProperty</span>&lt;*&gt;, value: <span class="type">String</span>?)</span></span> &#123;</span><br><span class="line">        thisRef.preferences.edit().putString(property.name, value).apply()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Hmm，乍一看有点摸不着头脑呢……简单的讲，AppPreferences这个class里面有两个delegate property，分别是userName和password。他们的get和set方法分别对应了SharedPreference中的getString和setString方法。纳尼？这么神奇？？？接着往下看</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span>: <span class="type">Application</span></span>() &#123;</span><br><span class="line">    <span class="keyword">val</span> pref <span class="keyword">by</span> lazy &#123; AppPreferences(<span class="keyword">this</span>) &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onCreate</span><span class="params">()</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate()</span><br><span class="line">        <span class="comment">// 其他代码</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在Application中定义了<code>pref</code>，注意它被<code>by lazy</code>修饰了，这样它就只在第一次调用的时候初始化，是不是很方便？</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">open</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseActivity</span>: <span class="type">AppCompatActivity</span></span>() &#123;</span><br><span class="line">    <span class="keyword">val</span> app <span class="keyword">by</span> lazy &#123; application <span class="keyword">as</span> App &#125;</span><br><span class="line">    <span class="keyword">val</span> pref <span class="keyword">by</span> lazy &#123; app.pref &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里我们在所有Activity的父类中引入了pref，即AppPreferences的实例。然后……就可以爽了~~</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyActivity</span>: <span class="type">BaseActivity</span></span>() &#123;</span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onCreate</span><span class="params">(savedInstanceState: <span class="type">Bundle</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState)</span><br><span class="line">        </span><br><span class="line">        pref.userName = <span class="string">"Who"</span></span><br><span class="line">        pref.password = <span class="string">"You"</span></span><br><span class="line">        Log.d(<span class="string">"userName"</span>, pref.userName)</span><br><span class="line">        Log.d(<span class="string">"password"</span>, pref.password)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>看到没有，用起来简直和field一毛一样！再也不用写<code>pref.edit().putString(&quot;userName&quot;, &quot;Who&quot;).apply()</code>了！！！老板再也不用担心我把SharedPreference的key写错了😂</p><h1 id="Lambdas"><a href="#Lambdas" class="headerlink" title="Lambdas"></a>Lambdas</h1><p>这个话题可就大了，一本书都讲不完，举个例子好了。<a href="https://github.com/ReactiveX/RxJava" target="_blank" rel="noopener">RxJava</a>现在很火，<a href="https://github.com/rengwuxian" target="_blank" rel="noopener">光头哥</a>写了一篇很好的<a href="http://gank.io/post/560e15be2dca930e00da1083" target="_blank" rel="noopener">文章</a>介绍了RxJava在android中的应用。抄一段代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">getUser(userId)</span><br><span class="line">    .doOnNext(<span class="keyword">new</span> Action1&lt;User&gt;() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">call</span><span class="params">(User user)</span> </span>&#123;</span><br><span class="line">            processUser(user);</span><br><span class="line">        &#125;)</span><br><span class="line">    .observeOn(AndroidSchedulers.mainThread())</span><br><span class="line">    .subscribe(<span class="keyword">new</span> Observer&lt;User&gt;() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onNext</span><span class="params">(User user)</span> </span>&#123;</span><br><span class="line">            userView.setUser(user);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCompleted</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onError</span><span class="params">(Throwable error)</span> </span>&#123;</span><br><span class="line">            <span class="comment">// Error handling</span></span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br></pre></td></tr></table></figure><p>嗯，链式调用，很清晰，但用Kotlin还可以更简洁：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">getUser(userId)</span><br><span class="line">    .doOnNext&#123; user -&gt; processUser(user) &#125;</span><br><span class="line">    .observeOn(AndroidSchedulers.mainThread())</span><br><span class="line">    .subscribe(&#123;</span><br><span class="line">        user -&gt; userView.setUser(user)</span><br><span class="line">    &#125;,&#123;</span><br><span class="line">        error -&gt; <span class="comment">// Error handling</span></span><br><span class="line">    &#125;)</span><br></pre></td></tr></table></figure><p>Done!收拾东西下班回家~</p><h1 id="风险"><a href="#风险" class="headerlink" title="风险"></a>风险</h1><p>我知道你在担心什么。Kotlin目前还是beta版，用于正式项目要评估风险。事实上在第一个beta版中就出现了在android 4.3设备上无法安装的bug，好在官方很快就修复了。怎么讲呢，这是需要项目组全体成员一起评估的问题。我之所以敢在实际项目中使用，一是因为项目比较简单，万一遇到不可解决的问题可以改用java写，反正Kotlin和java可以无缝混用；二是因为程序员就我一个人，胆子粗啊哈哈哈哈~</p><h1 id="感想"><a href="#感想" class="headerlink" title="感想"></a>感想</h1><p>整个项目做下来让我感到很愉悦，代码的优雅足以说服我冒一点点风险。开发过程中我常常在想，要是有一个包含各种扩展方法的library就好了，我就不用自己造小轮子了。但转念一下，我真的需要一个完整的library吗？那必然会像<a href="https://github.com/google/guava" target="_blank" rel="noopener">Guava</a>一样成为一个巨无霸，但实际用到的功能可能只有10%。在Kotlin的<a href="http://kotlinslackin.herokuapp.com/" target="_blank" rel="noopener">官方Slack</a>里<a href="https://github.com/jakewharton" target="_blank" rel="noopener">Jake Wharton</a>（没错就是那个大神）提了一个有趣的思路：在一个git里放了很多不同类别的扩展方法文件（比如ViewExt.kt，IntExt.kt），需要哪个文件就把它pull下来，而不是compile整个jar包。如果能用gradle实现就太方便了😆</p><h1 id="补充资料"><a href="#补充资料" class="headerlink" title="补充资料"></a>补充资料</h1><p><a href="http://antonioleiva.com/" target="_blank" rel="noopener">Antonio Leiva</a>写了一本书<a href="http://antonioleiva.com/kotlin-android-developers-book/" target="_blank" rel="noopener">Kotlin for Android Developers</a>，价格实惠干货满满，值得购买。配套的源码在<a href="https://github.com/antoniolg/Kotlin-for-Android-Developers" target="_blank" rel="noopener">https://github.com/antoniolg/Kotlin-for-Android-Developers</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;最近公司新开发了一个新App，android程序员就我一个人，爱咋写咋写~~于是我尝试用Kotlin完成了这个App。开发过程中越来越感动……太好用了！！！解决了android开发中的好多痛点！&lt;/p&gt;
&lt;h1 id=&quot;dp-sp-px间的转换&quot;&gt;&lt;a href=&quot;#dp-
      
    
    </summary>
    
      <category term="Kotlin" scheme="https://zhaoyouqi.github.io/categories/Kotlin/"/>
    
    
  </entry>
  
  <entry>
    <title>快速自定义tableViewCell</title>
    <link href="https://zhaoyouqi.github.io/2015/08/19/custom-table-view-cell/"/>
    <id>https://zhaoyouqi.github.io/2015/08/19/custom-table-view-cell/</id>
    <published>2015-08-19T12:47:01.000Z</published>
    <updated>2015-08-19T13:11:43.000Z</updated>
    
    <content type="html"><![CDATA[<p>最近因为工作需要开始学习iOS。在网上搜自动定义tableViewCell，出来的结果都是要新建xib文件和对应的class，然后在上面添加控件，再注册到tableViewController中。<br>这样做一来很繁琐，二来很容易在绑定outlet的时候抛异常（我就遇到了）。同事告诉我一个快捷的办法。</p><p>1、直接在StoryBoard里面将控件添加到Prototype Cell中。</p><p><img src="tableViewCell.png" alt="Image of tableViewCell"></p><p>2、新增一个Class（比如MyTableViewCell）继承UITableViewCell。</p><p>3、将Prototype Cell的Class设为MyTableViewCell。</p><p>4、按住<em>control</em>将控件拖到MyTableViewCell中，添加outlet。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">@property (nonatomic, strong) IBOutlet UILable *label;</span><br><span class="line">@property (nonatomic, strong) IBOutlet UIImageView *image;</span><br></pre></td></tr></table></figure></p><p>5、直接调用<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CELL_IDENTITY forIndexPath:indexPath];</span><br></pre></td></tr></table></figure></p><p>搞定~</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;最近因为工作需要开始学习iOS。在网上搜自动定义tableViewCell，出来的结果都是要新建xib文件和对应的class，然后在上面添加控件，再注册到tableViewController中。&lt;br&gt;这样做一来很繁琐，二来很容易在绑定outlet的时候抛异常（我就遇到了
      
    
    </summary>
    
      <category term="iOS" scheme="https://zhaoyouqi.github.io/categories/iOS/"/>
    
    
      <category term="tableViewCell" scheme="https://zhaoyouqi.github.io/tags/tableViewCell/"/>
    
  </entry>
  
  <entry>
    <title>Kotlin学习笔记（三）控制流程</title>
    <link href="https://zhaoyouqi.github.io/2015/08/08/learning-kotlin-3/"/>
    <id>https://zhaoyouqi.github.io/2015/08/08/learning-kotlin-3/</id>
    <published>2015-08-08T06:40:18.000Z</published>
    <updated>2015-08-08T08:06:59.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="If-Expression"><a href="#If-Expression" class="headerlink" title="If Expression"></a>If Expression</h1><p>在Kotlin中，<strong>if</strong>是一个表达式，它有返回类型。因此Kotlin中不再需要三元表达式。</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> max = <span class="keyword">if</span> (a &gt; b) a <span class="keyword">else</span> b</span><br></pre></td></tr></table></figure><p>if分支也可以是代码块（blocks），代码块的末尾是返回值。</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> max = <span class="keyword">if</span> (a &gt; b) &#123;</span><br><span class="line">  print(<span class="string">"Choose a"</span>)</span><br><span class="line">  a <span class="comment">// a会被赋值给max</span></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  print(<span class="string">"Choose b"</span>)</span><br><span class="line">  b <span class="comment">// b会被赋值给max</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="When-Expression"><a href="#When-Expression" class="headerlink" title="When Expression"></a>When Expression</h1><p><strong>when</strong>替代了switch语句，简单的例子像这样：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">when</span> (x) &#123;</span><br><span class="line">  <span class="number">1</span> -&gt; print(<span class="string">"x == 1"</span>)</span><br><span class="line">  <span class="number">2</span> -&gt; print(<span class="string">"x == 2"</span>)</span><br><span class="line">  <span class="keyword">else</span> -&gt; &#123;</span><br><span class="line">    print(<span class="string">"x is neither 1 or 2"</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果多个分支使用同样的处理逻辑，那么可以用逗号将它们结合起来：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">when</span> (x) &#123;</span><br><span class="line">  <span class="number">1</span>, <span class="number">2</span> -&gt; print(<span class="string">"x == 1 or x == 2"</span>)</span><br><span class="line">  <span class="keyword">else</span> -&gt; print(<span class="string">"otherwise"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>分支中除了使用常量（上例中的1、2），也可以使用函数（arbitrary expressions）：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">when</span> (x) &#123;</span><br><span class="line">  parseInt(s) -&gt; print(<span class="string">"s encodes x"</span>)</span><br><span class="line">  esle -&gt; print(<span class="string">"s does not encode x"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>也可以用<strong>in</strong>或<strong>!in</strong>判断一个值是否在一个范围（range）或集合（collection）中：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">when</span> (x) &#123;</span><br><span class="line">  <span class="keyword">in</span> <span class="number">1</span>...<span class="number">10</span> -&gt; print(<span class="string">"x is in the range"</span>)</span><br><span class="line">  <span class="keyword">in</span> validNumbers -&gt; print(<span class="string">"x is valid"</span>)</span><br><span class="line">  !<span class="keyword">in</span> <span class="number">10</span>...<span class="number">20</span> -&gt; print(<span class="string">"x is outside the range"</span>)</span><br><span class="line">  <span class="keyword">else</span> -&gt; print(<span class="string">"none of the above"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>还可以用<strong>is</strong>或<strong>!is</strong>来检查一个值是否是某个类型：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> hasPrefix = <span class="keyword">when</span>(x) &#123;</span><br><span class="line">  <span class="keyword">is</span> String -&gt; x.startsWith(<span class="string">"prefix"</span>) <span class="comment">// 注意这里s已经被自动转为String类型了</span></span><br><span class="line">  <span class="keyword">else</span> -&gt; <span class="literal">false</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>when还可以用来取代<strong>if-else if</strong>。如果没有传参数，那么分支条件就是简单的boolean表达式，当条件为true时分支就会被执行：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">when</span> &#123;</span><br><span class="line">  x.isOdd() -&gt; print(<span class="string">"x is odd"</span>)</span><br><span class="line">  x.isEven() -&gt; print(<span class="string">"x is even"</span>)</span><br><span class="line">  <span class="keyword">else</span> -&gt; print(<span class="string">"x is funny"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="For-Loops"><a href="#For-Loops" class="headerlink" title="For Loops"></a>For Loops</h1><p>感觉没什么特别的，跟Java一样：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (item <span class="keyword">in</span> collection)</span><br><span class="line">  print(item)</span><br><span class="line">  </span><br><span class="line"><span class="keyword">for</span> (i <span class="keyword">in</span> array.indices)</span><br><span class="line">  print(array[i])</span><br></pre></td></tr></table></figure><h1 id="While-Loops"><a href="#While-Loops" class="headerlink" title="While Loops"></a>While Loops</h1><p>也没什么特别的：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (x &gt; <span class="number">0</span>) &#123;</span><br><span class="line">  x--</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line">  <span class="keyword">val</span> y = retrieveData()</span><br><span class="line">&#125; <span class="keyword">while</span> (y != <span class="literal">null</span>) <span class="comment">// 注意虽然y是在大括号内部定义的，但这里仍然可以访问到</span></span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;If-Expression&quot;&gt;&lt;a href=&quot;#If-Expression&quot; class=&quot;headerlink&quot; title=&quot;If Expression&quot;&gt;&lt;/a&gt;If Expression&lt;/h1&gt;&lt;p&gt;在Kotlin中，&lt;strong&gt;if&lt;/stron
      
    
    </summary>
    
      <category term="Kotlin" scheme="https://zhaoyouqi.github.io/categories/Kotlin/"/>
    
    
      <category term="if" scheme="https://zhaoyouqi.github.io/tags/if/"/>
    
      <category term="when" scheme="https://zhaoyouqi.github.io/tags/when/"/>
    
      <category term="for" scheme="https://zhaoyouqi.github.io/tags/for/"/>
    
      <category term="while" scheme="https://zhaoyouqi.github.io/tags/while/"/>
    
  </entry>
  
  <entry>
    <title>Kotlin学习笔记（二）基本类型</title>
    <link href="https://zhaoyouqi.github.io/2015/07/28/learning-kotlin-2/"/>
    <id>https://zhaoyouqi.github.io/2015/07/28/learning-kotlin-2/</id>
    <published>2015-07-28T10:31:26.000Z</published>
    <updated>2015-07-30T09:51:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>如果你学过Ruby，你一定知道“万物皆对象”。Kotlin中也一样。即便数字(Numbers)在Kotlin中是基本类型，也可以这样写：<code>123.toString()</code></p><p>下面我们来看一下Kotlin中的基本类型。</p><h1 id="Numbers"><a href="#Numbers" class="headerlink" title="Numbers"></a>Numbers</h1><p>Kotlin提供的数字类型和Java很像：</p><table><thead><tr><th style="text-align:center"><strong>Type</strong></th><th style="text-align:center"><strong>Bitwidth</strong></th></tr></thead><tbody><tr><td style="text-align:center">Double</td><td style="text-align:center">64</td></tr><tr><td style="text-align:center">Float</td><td style="text-align:center">32</td></tr><tr><td style="text-align:center">Long</td><td style="text-align:center">64</td></tr><tr><td style="text-align:center">Int</td><td style="text-align:center">32</td></tr><tr><td style="text-align:center">Short</td><td style="text-align:center">16</td></tr><tr><td style="text-align:center">Byte</td><td style="text-align:center">8</td></tr></tbody></table><p>值得注意的是，定义Long形要用大写L：<strong>123L</strong></p><p>而16进制和二进制则和Java一样：<strong>0x0F</strong> <strong>0b0101</strong></p><p>8进制则不支持。</p><p>浮点型和Java一样，类似<strong>123.45</strong>默认为Double类型，想要Float类型则应写成<strong>123.45f</strong></p><p>在Java平台中，Number是基本类型。如果需要一个可空的数字（比如Int?）或者泛型，数字就会被装箱(boxed)：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> a: <span class="built_in">Int</span> = <span class="number">10000</span></span><br><span class="line">print(a == a) <span class="comment">// Prints 'true'</span></span><br><span class="line">print(a === a) <span class="comment">// Prints 'true'</span></span><br><span class="line"><span class="keyword">val</span> boxedA: <span class="built_in">Int</span>? = a</span><br><span class="line"><span class="keyword">val</span> anotherBoxedA: <span class="built_in">Int</span>? = a</span><br><span class="line">print(boxedA == anotherBoxedA) <span class="comment">// Prints 'true'</span></span><br><span class="line">print(boxedA === anotherBoxedA) <span class="comment">// !!!Prints 'false'!!!</span></span><br></pre></td></tr></table></figure><p><code>==</code>相当于调用<code>equals</code>方法。而<code>===</code>相当于Java中的<code>==</code>，用来比较引用对象。</p><p>注意在官方的<a href="http://kotlinlang.org/docs/reference/basic-types.html#representation" target="_blank" rel="noopener">Reference</a>中用的是即将被deprecated的<code>identityEquals</code>，会得到错误的结果，详情参看 <a href="http://stackoverflow.com/questions/31666155/kotlins-identityequals-not-working-as-expected" target="_blank" rel="noopener">http://stackoverflow.com/questions/31666155/kotlins-identityequals-not-working-as-expected</a></p><p>Kotlin中，数字类型<strong>不能</strong>像Java一样隐式(implicitly)转换类型。</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> a:<span class="built_in">Int</span>? = <span class="number">1</span></span><br><span class="line"><span class="keyword">val</span> b:<span class="built_in">Long</span>? = a <span class="comment">// 这里会编译报错，Int不能转换成Long</span></span><br><span class="line">print(a == b) <span class="comment">// 这里会编译报错，Int和Long不能进行比较</span></span><br></pre></td></tr></table></figure><p>而所有的数字类型都有如下显示方法：</p><ul><li>toByte()</li><li>toShort()</li><li>toInt()</li><li>toLong()</li><li>toFloat()</li><li>toDouble()</li><li>toChar()</li></ul><p>写惯Java的人可能觉得这样会很不方便，但事情也没有那么糟糕。因为运算符可以帮我们隐式的转换数字类型：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> l = <span class="number">1</span>.toLong() + <span class="number">3</span> <span class="comment">// Long + Int =&gt; Long</span></span><br></pre></td></tr></table></figure><p>仅Int和Long可以进行位运算。运算符与Java不同：</p><ul><li>shl(bits) 有符号的左位移，在Java中是<code>&lt;&lt;</code></li><li>shr(bits) 有符号的右位移，在Java中是<code>&gt;&gt;</code></li><li>ushr(bits) 无符号的右位移，在Java中是<code>&gt;&gt;&gt;</code></li><li>and(bits) 与，在Java中是<code>&amp;</code></li><li>or(bits) 或，在Java中是<code>|</code></li><li>xor(bits) 异或，在Java中是<code>^</code></li><li>inv() 非，在Java中是<code>~</code></li></ul><h1 id="Characters"><a href="#Characters" class="headerlink" title="Characters"></a>Characters</h1><p>Int不能直接转为Char，下面的代码会编译报错：</p><p><code>val a:Char = 1</code></p><p>但是Char可以通过<code>toInt()</code>方法转为Int。</p><p>同数字一样，如果需要一个可空类型（Char?），那么这个Char会被自动装箱（boxed）。</p><h1 id="Booleans"><a href="#Booleans" class="headerlink" title="Booleans"></a>Booleans</h1><p>没什么好说的，也是自动装箱的问题。</p><h1 id="Arrays"><a href="#Arrays" class="headerlink" title="Arrays"></a>Arrays</h1><p>Kotlin中的数组写法与Java区别很大，有专门的<code>Array</code>类来创建和操作数组。</p><p>如果我想创建一个[1, 2, 3]数组，可以这样写：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> a:Array&lt;<span class="built_in">Int</span>&gt; = arrayOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>) <span class="comment">// 注意这里的数字装箱了</span></span><br><span class="line">a[<span class="number">0</span>] = a[<span class="number">1</span>] + a[<span class="number">2</span>] <span class="comment">// 这里的操作与Java一样</span></span><br></pre></td></tr></table></figure><p>还可以像这样写：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Creates an Array&lt;String&gt; with values ["0", "1", "4", "9", "16"]</span></span><br><span class="line"><span class="keyword">val</span> asc = Array(<span class="number">5</span>, &#123;i -&gt; (i * i).toString()&#125;)</span><br></pre></td></tr></table></figure><p>如果想要一个空数组，可以使用<code>arrayOfNulls(size: Int)</code>方法。</p><p>如果不想装箱，Kotlin也提供一些原子类型（primitive type）的数组，如<code>IntArray</code>，<code>ShortArray</code>等。这些类型没有继承<code>Array</code>类型，但是拥有相同的方法。每个类型都有对应的工厂方法：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> x: IntArray = intArrayOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line">x[<span class="number">0</span>] = x[<span class="number">1</span>] + x[<span class="number">2</span>]</span><br></pre></td></tr></table></figure><h1 id="Strings"><a href="#Strings" class="headerlink" title="Strings"></a>Strings</h1><p><code>s.[0]</code>相当于Java中的<code>s.charAt(0)</code></p><p>还可以使用iterator：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (c <span class="keyword">in</span> str) &#123;</span><br><span class="line">    println(c)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Kotlin包含两种类型的字符串：与Java字符串相同的escaped strings和新的raw strings。escaped strings中的换行符是<code>\n</code>，而raw strings就厉害了。看这个例子：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> text = <span class="string">"""</span></span><br><span class="line"><span class="string">    for (c in "foo")</span></span><br><span class="line"><span class="string">    print(c)</span></span><br><span class="line"><span class="string">"""</span></span><br></pre></td></tr></table></figure><p>注意第二行和第三行都是字符串，它们被包含在三个引号中。</p><p>Kotlin中再也不用写繁琐的<code>String.format</code>了，只需要一个<code>$</code>：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> i = <span class="number">10</span></span><br><span class="line"><span class="keyword">val</span> s = <span class="string">"i = <span class="variable">$i</span>"</span> <span class="comment">// evaluates to "i = 10"</span></span><br></pre></td></tr></table></figure><p>甚至可以像这样写表达式：</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> s = <span class="string">"abc"</span></span><br><span class="line"><span class="keyword">val</span> str = <span class="string">"<span class="variable">$s</span>.length is <span class="subst">$&#123;s.length&#125;</span>"</span> <span class="comment">// evaluates to "abc.length is 3"</span></span><br></pre></td></tr></table></figure><p><em>这篇是用<a href="http://typora.io/" target="_blank" rel="noopener">Typora</a>写的，确实很好用，而且是国人出品！</em></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;如果你学过Ruby，你一定知道“万物皆对象”。Kotlin中也一样。即便数字(Numbers)在Kotlin中是基本类型，也可以这样写：&lt;code&gt;123.toString()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;下面我们来看一下Kotlin中的基本类型。&lt;/p&gt;
&lt;h1 id=&quot;
      
    
    </summary>
    
      <category term="Kotlin" scheme="https://zhaoyouqi.github.io/categories/Kotlin/"/>
    
    
      <category term="number" scheme="https://zhaoyouqi.github.io/tags/number/"/>
    
      <category term="character" scheme="https://zhaoyouqi.github.io/tags/character/"/>
    
      <category term="boolean" scheme="https://zhaoyouqi.github.io/tags/boolean/"/>
    
      <category term="string" scheme="https://zhaoyouqi.github.io/tags/string/"/>
    
      <category term="array" scheme="https://zhaoyouqi.github.io/tags/array/"/>
    
  </entry>
  
  <entry>
    <title>Kotlin学习笔记（一）基本语法</title>
    <link href="https://zhaoyouqi.github.io/2015/07/24/learning-kotlin-1/"/>
    <id>https://zhaoyouqi.github.io/2015/07/24/learning-kotlin-1/</id>
    <published>2015-07-24T10:37:27.000Z</published>
    <updated>2015-07-25T12:01:07.000Z</updated>
    
    <content type="html"><![CDATA[<p>先提个问题</p><h1 id="Android程序员为什么要学Kotlin？"><a href="#Android程序员为什么要学Kotlin？" class="headerlink" title="Android程序员为什么要学Kotlin？"></a>Android程序员为什么要学Kotlin？</h1><ol><li>Java不争气。在1.5时代Java或许还能和C#谈笑风生，而现在C#不知道比Java高明到哪里去了。虽然Java8中也加入了闭包等有用的特性，但Android又不支持……</li><li><a href="https://github.com/ReactiveX/RxJava" target="_blank" rel="noopener">RxJava</a>。想象这样一个需求：用户连续点击某个区域10次且每次间隔不超过500毫秒，则触发一个彩蛋。<br>实现这个需求很容易，但代码会很混乱。我们得自己控制计时器，保存时间戳，可能还会有令人讨厌的空接口。如果用RxJava来做，几行代码就能搞定。如果再配合Kotlin的函数式编程，简直不能更爽~<br>关于RxJava的使用，网上已经有很多了。我有空了可能也会写一些。</li><li>如果你尝试了<a href="https://developer.android.com/tools/data-binding/guide.html" target="_blank" rel="noopener">Android Databinding</a>，有没有发现External Libraries里面多了kotlin-runtime和kotlin-stdlib？<br>Google内部正是用Kotlin开发的Databinding。虽然短期内Kotlin还不太可能成为Android官方开发语言，但至少这是个好的迹象。</li></ol><h1 id="为什么是Kotlin？"><a href="#为什么是Kotlin？" class="headerlink" title="为什么是Kotlin？"></a>为什么是Kotlin？</h1><p>现在替代Java的选择有很多，Scala、Groovy都能写Android，甚至微软最新发布的Visual Studio都能进来插一脚，为什么要选<a href="http://kotlinlang.org/" target="_blank" rel="noopener">Kotlin</a>呢？<br>这又是另一个很大的话题了。请参看大神Jake Wharton的这篇<a href="https://docs.google.com/document/d/1ReS3ep-hjxWA8kZi0YqDbEhCqTt29hG8P44aA9W0DM8/edit" target="_blank" rel="noopener">文章</a>（要翻墙）。</p><p>那么进入正题。我的学习步骤基本是按照官方的<a href="http://kotlinlang.org/docs/reference/" target="_blank" rel="noopener">Reference</a>来，所以可能写着写着就成了翻译官方文档了……哈哈哈哈</p><h1 id="Function"><a href="#Function" class="headerlink" title="Function"></a>Function</h1><p>最开始学C的时候，我们会说为了实现XXX写个函数（Function），学Java就变成了写个方法（Method），现在Kotlin又变成函数了……<br>所以我后面提到Java的时候会说Method，提到Kotlin的时候会说Function，其实都是一个意思~</p><p>先看下Java中的写法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sum</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>然后是Kotlin<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">sum</span><span class="params">(a: <span class="type">Int</span>, b: <span class="type">Int</span>)</span></span>: <span class="built_in">Int</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> a + b</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>区别：</p><ol><li>用fun开头</li><li>数据类型写在后面</li><li>结尾不用写分号</li></ol><p>这语法让我想起了Action Script，写惯了Java再来写Kotlin总觉得有些别扭……</p><p>如果想简洁一点可以这样写在一行里，并且返回值类型可以是隐式（inferred）的。不过我个人不太喜欢这样写。</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">sum</span><span class="params">(a: <span class="type">Int</span>, b: <span class="type">Int</span>)</span></span> = a + b</span><br></pre></td></tr></table></figure><p>如果是public function就必须显式的标明返回类型</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">fun</span> <span class="title">sum</span><span class="params">(a: <span class="type">Int</span>, b: <span class="type">Int</span>)</span></span>: <span class="built_in">Int</span> = a + b</span><br></pre></td></tr></table></figure><p>没有返回值则用<strong>Unit</strong>关键字</p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">printSum</span><span class="params">(a: <span class="type">Int</span>, b: <span class="type">Int</span>)</span></span>: <span class="built_in">Unit</span> &#123;</span><br><span class="line">    print(a + b)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>即使函数是public的，Unit也可以省略掉<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">fun</span> <span class="title">printSum</span><span class="params">(a: <span class="type">Int</span>, b: <span class="type">Int</span>)</span></span> &#123;</span><br><span class="line">    print(a + b)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="local-variables"><a href="#local-variables" class="headerlink" title="local variables"></a>local variables</h1><p>只读变量（read-only）用<strong>val</strong>定义<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> a: <span class="built_in">Int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">val</span> b = <span class="number">1</span> <span class="comment">// 如果赋了初始值值，可以省略数据类型</span></span><br><span class="line"><span class="keyword">val</span> c: <span class="built_in">Int</span> <span class="comment">// 否则需要指定类型</span></span><br><span class="line">c = <span class="number">1</span></span><br></pre></td></tr></table></figure></p><p>可变变量（mutable）用<strong>var</strong>定义<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> x = <span class="number">5</span></span><br><span class="line">x += <span class="number">1</span></span><br></pre></td></tr></table></figure></p><h1 id="string-templates"><a href="#string-templates" class="headerlink" title="string templates"></a>string templates</h1><p>用<strong>${}</strong>包住变量就行了，这比Java中的String.valueof方便多了<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">val</span> a = <span class="number">1</span></span><br><span class="line">print(<span class="string">"Value of a: <span class="subst">$&#123;a&#125;</span>"</span>&#125;</span><br></pre></td></tr></table></figure></p><h1 id="conditional-expressions"><a href="#conditional-expressions" class="headerlink" title="conditional expressions"></a>conditional expressions</h1><p>看起来有点像三元表达式，但可读性更强<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">max</span><span class="params">(a: <span class="type">Int</span>, b: <span class="type">Int</span>)</span></span> = <span class="keyword">if</span> (a &gt; b) a <span class="keyword">else</span> b</span><br></pre></td></tr></table></figure></p><h1 id="nullable-values"><a href="#nullable-values" class="headerlink" title="nullable values"></a>nullable values</h1><p>写java的同学一定受够了NullPointerException吧。Kotlin与Java很大不同的一点就是显示的标明了value是否可能为空。<br>如果可能为空，则用<strong>?</strong>注明<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> x: <span class="built_in">Int</span>?</span><br></pre></td></tr></table></figure></p><p>先知道有这么回事就行了，后面的章节会详细说明。</p><h1 id="type-checks-amp-automatic-casts"><a href="#type-checks-amp-automatic-casts" class="headerlink" title="type checks &amp; automatic casts"></a>type checks &amp; automatic casts</h1><p>和C#一样，用<strong>is</strong>操作符来判断变量的类型<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">getStringLength</span><span class="params">(obj: <span class="type">Any</span>)</span></span>: <span class="built_in">Int</span>? &#123;</span><br><span class="line">    <span class="keyword">if</span> (obj <span class="keyword">is</span> String) &#123;</span><br><span class="line">        <span class="keyword">return</span> obj.length <span class="comment">// 注意这里obj已经自动转成String类型了，所以可以调用length方法</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span> <span class="comment">// 这里obj依然是Any类型</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="loop"><a href="#loop" class="headerlink" title="loop"></a>loop</h1><p>和Java相比是把冒号改成了<strong>in</strong><br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">main</span><span class="params">(args: <span class="type">Array</span>&lt;<span class="type">String</span>&gt;)</span></span> &#123;</span><br><span class="line">    <span class="keyword">for</span> (arg <span class="keyword">in</span> args)</span><br><span class="line">        print(arg)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="while-loop"><a href="#while-loop" class="headerlink" title="while loop"></a>while loop</h1><p>和Java基本一样<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">main</span><span class="params">(args: <span class="type">Array</span>&lt;<span class="type">String</span>&gt;)</span></span> &#123; </span><br><span class="line">    <span class="keyword">var</span> i = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> (i &lt; args.size())</span><br><span class="line">        print(args[i++]) </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="when-expression"><a href="#when-expression" class="headerlink" title="when expression"></a>when expression</h1><p><strong>when</strong>可以类比Java中的<strong>switch case</strong>，但是更加强大，可以接受各种操作符<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">fun</span> <span class="title">cases</span><span class="params">(obj: <span class="type">Any</span>)</span></span> &#123; </span><br><span class="line">    <span class="keyword">when</span> (obj) &#123;</span><br><span class="line">        <span class="number">1</span> -&gt; print(<span class="string">"One"</span>)</span><br><span class="line">        <span class="string">"Hello"</span> -&gt; print(<span class="string">"Greeting"</span>)</span><br><span class="line">        <span class="keyword">is</span> <span class="built_in">Long</span> -&gt; print(<span class="string">"Long"</span>)</span><br><span class="line">        !<span class="keyword">is</span> String -&gt; print(<span class="string">"Not a string"</span>)</span><br><span class="line">        <span class="keyword">else</span> -&gt; print(<span class="string">"Unknow"</span>)</span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h1 id="ranges"><a href="#ranges" class="headerlink" title="ranges"></a>ranges</h1><p><strong>in</strong>操作符太好用了，我就不贴对应的Java代码了，因为想想就觉得麻烦……<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (x <span class="keyword">in</span> <span class="number">1</span>..y-<span class="number">1</span>) </span><br><span class="line">    print(<span class="string">"OK"</span>)</span><br><span class="line">    </span><br><span class="line"><span class="keyword">if</span> (x !<span class="keyword">in</span> <span class="number">0</span>..array.lastIndex) </span><br><span class="line">    print(<span class="string">"Out"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (x <span class="keyword">in</span> <span class="number">1</span>..<span class="number">5</span>) </span><br><span class="line">    print(x)</span><br></pre></td></tr></table></figure></p><h1 id="collections"><a href="#collections" class="headerlink" title="collections"></a>collections</h1><p>collections配合in使用很方便<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (text <span class="keyword">in</span> names) <span class="comment">// 这里调用了names.contains(text) </span></span><br><span class="line">    print(<span class="string">"Yes"</span>)</span><br></pre></td></tr></table></figure></p><p>如果配合函数式编程和Lambdas表达式就不能更爽……<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">names filter &#123; it.startsWith(<span class="string">"A"</span>) &#125; </span><br><span class="line">      sortBy &#123; it &#125; </span><br><span class="line">      map &#123; it.toUpperCase() &#125; </span><br><span class="line">      forEach &#123; print(it) &#125;</span><br></pre></td></tr></table></figure></p><p>注意这里函数前面没有小圆点（<strong>.</strong>），这个大概是特有的简写，后面再慢慢学吧~</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;先提个问题&lt;/p&gt;
&lt;h1 id=&quot;Android程序员为什么要学Kotlin？&quot;&gt;&lt;a href=&quot;#Android程序员为什么要学Kotlin？&quot; class=&quot;headerlink&quot; title=&quot;Android程序员为什么要学Kotlin？&quot;&gt;&lt;/a&gt;Android
      
    
    </summary>
    
      <category term="Kotlin" scheme="https://zhaoyouqi.github.io/categories/Kotlin/"/>
    
    
      <category term="syntax" scheme="https://zhaoyouqi.github.io/tags/syntax/"/>
    
  </entry>
  
  <entry>
    <title>在Android中将文本转为曲线</title>
    <link href="https://zhaoyouqi.github.io/2015/07/18/android-convert-text-to-curves/"/>
    <id>https://zhaoyouqi.github.io/2015/07/18/android-convert-text-to-curves/</id>
    <published>2015-07-18T08:09:27.000Z</published>
    <updated>2015-07-20T10:24:14.000Z</updated>
    
    <content type="html"><![CDATA[<p>最近在Android平台上生成PDF文件时，碰到了内嵌字体的问题。大部分打印厂的机器是不支持PDF内嵌字体的，<br>所以要想将文字按指定的字体印刷出来，只能<a href="http://baike.baidu.com/subview/8597409/8551998.htm" target="_blank" rel="noopener">转曲</a>。</p><h1 id="转曲"><a href="#转曲" class="headerlink" title="转曲"></a>转曲</h1><p>转曲的意思就是把文字转成曲线线段，可以想象成把文字的轮廓勾勒出来。<br>Android中的<a href="http://developer.android.com/reference/android/graphics/Paint.html#getTextPath(java.lang.String, int, int, float, float, android.graphics.Path" target="_blank" rel="noopener">Paint.getTextPath</a><br>方法可以获得String的Path，而<a href="http://developer.android.com/reference/android/graphics/PathMeasure.html" target="_blank" rel="noopener">PathMeasure</a>可以获得Path中的关键点。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Paint paint = <span class="keyword">new</span> Paint(Paint.ANTI_ALIAS_FLAG);</span><br><span class="line">paint.setTextSize(yourTextSize);</span><br><span class="line">Path fontPath = <span class="keyword">new</span> Path();</span><br><span class="line">paint.getTextPath(yourText, <span class="number">0</span>, yourText.length(), <span class="number">0f</span>, paint.getFontSpacing(), fontPath);</span><br><span class="line">PathMeasure pathMeasure = <span class="keyword">new</span> PathMeasure(fontPath, <span class="keyword">false</span>);</span><br><span class="line"><span class="keyword">float</span>[] pos = <span class="keyword">new</span> <span class="keyword">float</span>[<span class="number">2</span>]; <span class="comment">// pos用来存放每一个关键点</span></span><br><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line">    <span class="keyword">float</span> distance = <span class="number">0f</span>;</span><br><span class="line">    <span class="keyword">while</span> (distance &lt; pathMeasure.getLength()) &#123;</span><br><span class="line">        pathMeasure.getPosTan(distance, pos, <span class="keyword">null</span>);</span><br><span class="line">        <span class="comment">// 在这里将pos设置到你自己的曲线里</span></span><br><span class="line">        <span class="comment">// 我使用PDFjet生成PDF，那么这里就将pos保存到com.pdfjet.Point中</span></span><br><span class="line">        distance += <span class="number">0.1f</span>; <span class="comment">// 0.1f是每个关键点的间隔。间隔越大轮廓越粗糙，反之越细腻。如果设的过小会影响性能。</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125; <span class="keyword">while</span> (pathMeasure.nextContour());<span class="comment">// nextContour会跳到下一段曲线</span></span><br></pre></td></tr></table></figure><h1 id="计算曲线嵌套层数"><a href="#计算曲线嵌套层数" class="headerlink" title="计算曲线嵌套层数"></a>计算曲线嵌套层数</h1><p>得到文字曲线后还不能直接印刷，因为我们只是得到了文字的轮廓，还得往轮廓里填色。<br>当然不能所有轮廓都填黑色，这样文字会变成一坨一坨的黑色……<br>我们需要计算每条曲线被多少条曲线全包围（注意我们现在得到的曲线全都是头尾相连的）。<br>如果被偶数条曲线（包含0条）包围，则填黑色；如果被奇数条曲线包围，则填白色（底色）。<br>Android或Java本身不提供这样的方法，还好已经有人帮我们研究出方法了，详细参看<a href="http://alienryderflex.com/polygon/。" target="_blank" rel="noopener">http://alienryderflex.com/polygon/。</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Polygon</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Polygon coodinates.</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span>[] polyY, polyX;</span><br><span class="line"> </span><br><span class="line">    <span class="comment">// Number of sides in the polygon.</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> polySides;</span><br><span class="line"> </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Default constructor.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> px Polygon y coods.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> py Polygon x coods.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> ps Polygon sides count.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Polygon</span><span class="params">( <span class="keyword">float</span>[] px, <span class="keyword">float</span>[] py, <span class="keyword">int</span> ps )</span> </span>&#123;</span><br><span class="line">        polyX = px;</span><br><span class="line">        polyY = py;</span><br><span class="line">        polySides = ps;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Checks if the Polygon contains a point.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@see</span> "http://alienryderflex.com/polygon/"</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> x Point horizontal pos.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> y Point vertical pos.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> Point is in Poly flag.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">contains</span><span class="params">( <span class="keyword">float</span> x, <span class="keyword">float</span> y )</span> </span>&#123;</span><br><span class="line">        <span class="keyword">boolean</span> oddTransitions = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">for</span>( <span class="keyword">int</span> i = <span class="number">0</span>, j = polySides -<span class="number">1</span>; i &lt; polySides; j = i++ ) &#123;</span><br><span class="line">            <span class="keyword">if</span>((Float.compare(polyY[i], y) &lt; <span class="number">0</span> &amp;&amp; Float.compare(polyY[j], y) &gt;= <span class="number">0</span> ) || ( Float.compare(polyY[j], y) &lt; <span class="number">0</span> &amp;&amp; Float.compare(polyY[i], y) &gt;= <span class="number">0</span>))&#123;</span><br><span class="line">                <span class="keyword">if</span>(Float.compare(polyX[i] + ( y - polyY[i] ) / ( polyY[j] - polyY[i] ) * ( polyX[j] - polyX[i] ), x) &lt; <span class="number">0</span> ) &#123;</span><br><span class="line">                    oddTransitions = !oddTransitions;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> oddTransitions;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Polygon的构造方法前两个参数填多边形（可以将曲线理解为多边形）横坐标和纵坐标的数组，第三个参数填多边形的边数，即坐标的个数。<br>contains方法即可检查某个点是否在这个多边形中。<br>那么思路就很简单了：如果曲线A的任一关键点不在曲线B中，B就没有包围A。<br>代码就不贴了。</p><h1 id="填色"><a href="#填色" class="headerlink" title="填色"></a>填色</h1><p>如上所述，如果被偶数条曲线包围，填黑色；被奇数条曲线包围，填白色。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> nestingNum = surroundedNumber(pathPointsList, pathPoints);</span><br><span class="line"><span class="keyword">if</span> (nestingNum % <span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line">    paint.setColor(Color.BLACK);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    paint.setColor(Color.WHITE);</span><br><span class="line">&#125;</span><br><span class="line">canvas.drawPath(path, paint);</span><br></pre></td></tr></table></figure><p>大功告成~</p><p>详细代码请参考<a href="https://gist.github.com/qq157755587/32e927e77e259cd84631" target="_blank" rel="noopener">https://gist.github.com/qq157755587/32e927e77e259cd84631</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;最近在Android平台上生成PDF文件时，碰到了内嵌字体的问题。大部分打印厂的机器是不支持PDF内嵌字体的，&lt;br&gt;所以要想将文字按指定的字体印刷出来，只能&lt;a href=&quot;http://baike.baidu.com/subview/8597409/8551998.ht
      
    
    </summary>
    
      <category term="Android" scheme="https://zhaoyouqi.github.io/categories/Android/"/>
    
    
      <category term="PDF" scheme="https://zhaoyouqi.github.io/tags/PDF/"/>
    
      <category term="Paint" scheme="https://zhaoyouqi.github.io/tags/Paint/"/>
    
      <category term="Path" scheme="https://zhaoyouqi.github.io/tags/Path/"/>
    
  </entry>
  
</feed>
